Build imime.la on Windows.
authorMatthias Clasen <mclasen@redhat.com>
Thu, 26 Aug 2004 14:29:25 +0000 (14:29 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Thu, 26 Aug 2004 14:29:25 +0000 (14:29 +0000)
2004-08-26  Matthias Clasen  <mclasen@redhat.com>

* modules/input/Makefile.am: Build imime.la on Windows.

* modules/input/gtkimcontextime.h:
* modules/input/gtkimcontextime.c:
* modules/input/imime.c:
* modules/input/imm-extra.h: Add the IME input method for Win32
written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
hosted at http://sourceforge.jp/projects/imime.  (#135195)

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-6
ChangeLog.pre-2-8
modules/input/Makefile.am
modules/input/gtkimcontextime.c [new file with mode: 0644]
modules/input/gtkimcontextime.h [new file with mode: 0644]
modules/input/imime.c [new file with mode: 0644]
modules/input/imm-extra.h [new file with mode: 0644]

index 3d05a76c2fc2fc88f9a9a91ee1fd4b90552e328a..5bedc380dda0750d79b3f2a1468fa6211c9e374e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2004-08-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * modules/input/Makefile.am: Build imime.la on Windows.
+
+       * modules/input/gtkimcontextime.h: 
+       * modules/input/gtkimcontextime.c: 
+       * modules/input/imime.c: 
+       * modules/input/imm-extra.h: Add the IME input method for Win32
+       written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+       hosted at http://sourceforge.jp/projects/imime.  (#135195)
+
 2004-08-26  Bill Haneman <billh@gnome.org>
 
        * gtk/gtktreeview:
index 3d05a76c2fc2fc88f9a9a91ee1fd4b90552e328a..5bedc380dda0750d79b3f2a1468fa6211c9e374e 100644 (file)
@@ -1,3 +1,14 @@
+2004-08-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * modules/input/Makefile.am: Build imime.la on Windows.
+
+       * modules/input/gtkimcontextime.h: 
+       * modules/input/gtkimcontextime.c: 
+       * modules/input/imime.c: 
+       * modules/input/imm-extra.h: Add the IME input method for Win32
+       written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+       hosted at http://sourceforge.jp/projects/imime.  (#135195)
+
 2004-08-26  Bill Haneman <billh@gnome.org>
 
        * gtk/gtktreeview:
index 3d05a76c2fc2fc88f9a9a91ee1fd4b90552e328a..5bedc380dda0750d79b3f2a1468fa6211c9e374e 100644 (file)
@@ -1,3 +1,14 @@
+2004-08-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * modules/input/Makefile.am: Build imime.la on Windows.
+
+       * modules/input/gtkimcontextime.h: 
+       * modules/input/gtkimcontextime.c: 
+       * modules/input/imime.c: 
+       * modules/input/imm-extra.h: Add the IME input method for Win32
+       written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+       hosted at http://sourceforge.jp/projects/imime.  (#135195)
+
 2004-08-26  Bill Haneman <billh@gnome.org>
 
        * gtk/gtktreeview:
index 3d05a76c2fc2fc88f9a9a91ee1fd4b90552e328a..5bedc380dda0750d79b3f2a1468fa6211c9e374e 100644 (file)
@@ -1,3 +1,14 @@
+2004-08-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * modules/input/Makefile.am: Build imime.la on Windows.
+
+       * modules/input/gtkimcontextime.h: 
+       * modules/input/gtkimcontextime.c: 
+       * modules/input/imime.c: 
+       * modules/input/imm-extra.h: Add the IME input method for Win32
+       written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+       hosted at http://sourceforge.jp/projects/imime.  (#135195)
+
 2004-08-26  Bill Haneman <billh@gnome.org>
 
        * gtk/gtktreeview:
index 70fcab66dd3630002bc5211ef538677c55851c36..b234f0b2e6403b73d437d78aa7da11ceed977a5d 100644 (file)
@@ -75,6 +75,18 @@ im_ipa_la_LDFLAGS = -rpath $(moduledir) -avoid-version -module $(no_undefined)
 im_ipa_la_SOURCES = imipa.c
 im_ipa_la_LIBADD = $(LDADDS)
 
+
+im_ime_la_LDFLAGS = -rpath $(moduledir) -avoid-version -module $(no_undefined)
+im_ime_la_SOURCES = \
+       gtkimcontextime.c \
+       gtkimcontextime.h \
+       imime.c           \
+       imm-extra.h
+im_ime_la_LIBADD = $(LDADDS)
+if PLATFORM_WIN32
+IM_IME_MODULE=im-ime.la 
+endif
+
 if CROSS_COMPILING
 RUN_QUERY_IMMODULES_TEST=false
 else
@@ -115,7 +127,8 @@ module_LTLIBRARIES =                                \
        im-thai-broken.la                       \
        im-ti-er.la                             \
        im-ti-et.la                             \
-       im-viqr.la
+       im-viqr.la                              \
+       $(IM_IME_MODULE)
 
 gtk.immodules: Makefile.am $(module_LTLIBRARIES)
        $(top_builddir)/gtk/gtk-query-immodules-2.0 $(module_LTLIBRARIES) > gtk.immodules
diff --git a/modules/input/gtkimcontextime.c b/modules/input/gtkimcontextime.c
new file mode 100644 (file)
index 0000000..b441d53
--- /dev/null
@@ -0,0 +1,1103 @@
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ * Copyright (C) 2003-2004 Kazuki IWAMOTO
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+/*
+ *  Please see the following site for the detail of Windows IME API.
+ *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/appendix/hh/appendix/imeimes2_35ph.asp
+ */
+
+#include "gtkimcontextime.h"
+
+#include "imm-extra.h"
+
+#include <gdk/gdkwin32.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwidget.h>
+
+/* avoid warning */
+#ifdef STRICT
+# undef STRICT
+# include <pango/pangowin32.h>
+# ifndef STRICT
+#   define STRICT 1
+# endif
+#else /* STRICT */
+#   include <pango/pangowin32.h>
+#endif /* STRICT */
+
+/* #define BUFSIZE 4096 */
+
+#define FREE_PREEDIT_BUFFER(ctx) \
+{                                \
+  g_free((ctx)->priv->comp_str); \
+  g_free((ctx)->priv->read_str); \
+  (ctx)->priv->comp_str = NULL;  \
+  (ctx)->priv->read_str = NULL;  \
+  (ctx)->priv->comp_str_len = 0; \
+  (ctx)->priv->read_str_len = 0; \
+}
+
+
+struct _GtkIMContextIMEPrivate
+{
+  /* save IME context when the client window is focused out */
+  DWORD conversion_mode;
+  DWORD sentence_mode;
+
+  LPVOID comp_str;
+  DWORD comp_str_len;
+  LPVOID read_str;
+  DWORD read_str_len;
+};
+
+
+/* GObject class methods */
+static void gtk_im_context_ime_class_init (GtkIMContextIMEClass *class);
+static void gtk_im_context_ime_init       (GtkIMContextIME      *context_ime);
+static void gtk_im_context_ime_dispose    (GObject              *obj);
+static void gtk_im_context_ime_finalize   (GObject              *obj);
+
+static void gtk_im_context_ime_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec);
+static void gtk_im_context_ime_get_property (GObject      *object,
+                                             guint         prop_id,
+                                             GValue       *value,
+                                             GParamSpec   *pspec);
+
+/* GtkIMContext's virtual functions */
+static void gtk_im_context_ime_set_client_window   (GtkIMContext *context,
+                                                    GdkWindow    *client_window);
+static gboolean gtk_im_context_ime_filter_keypress (GtkIMContext   *context,
+                                                    GdkEventKey    *event);
+static void gtk_im_context_ime_reset               (GtkIMContext   *context);
+static void gtk_im_context_ime_get_preedit_string  (GtkIMContext   *context,
+                                                    gchar         **str,
+                                                    PangoAttrList **attrs,
+                                                    gint           *cursor_pos);
+static void gtk_im_context_ime_focus_in            (GtkIMContext   *context);
+static void gtk_im_context_ime_focus_out           (GtkIMContext   *context);
+static void gtk_im_context_ime_set_cursor_location (GtkIMContext   *context,
+                                                    GdkRectangle   *area);
+static void gtk_im_context_ime_set_use_preedit     (GtkIMContext   *context,
+                                                    gboolean        use_preedit);
+
+/* GtkIMContextIME's private functions */
+static void gtk_im_context_ime_set_preedit_font (GtkIMContext    *context,
+                                                 PangoFont       *font);
+static GdkFilterReturn
+            gtk_im_context_ime_message_filter   (GdkXEvent       *xevent,
+                                                 GdkEvent        *event,
+                                                 gpointer         data);
+static void get_window_position                 (GdkWindow       *win,
+                                                 gint            *x,
+                                                 gint            *y);
+static void cb_client_widget_hierarchy_changed  (GtkWidget       *widget,
+                                                 GtkWidget       *widget2,
+                                                 GtkIMContextIME *context_ime);
+
+GType gtk_type_im_context_ime = 0;
+static GObjectClass *parent_class;
+
+
+void
+gtk_im_context_ime_register_type (GTypeModule *type_module)
+{
+  static const GTypeInfo im_context_ime_info = {
+    sizeof (GtkIMContextIMEClass),
+    (GBaseInitFunc) NULL,
+    (GBaseFinalizeFunc) NULL,
+    (GClassInitFunc) gtk_im_context_ime_class_init,
+    NULL,                       /* class_finalize */
+    NULL,                       /* class_data */
+    sizeof (GtkIMContextIME),
+    0,
+    (GInstanceInitFunc) gtk_im_context_ime_init,
+  };
+
+  gtk_type_im_context_ime =
+    g_type_module_register_type (type_module,
+                                 GTK_TYPE_IM_CONTEXT,
+                                 "GtkIMContextIME", &im_context_ime_info, 0);
+}
+
+static void
+gtk_im_context_ime_class_init (GtkIMContextIMEClass *class)
+{
+  GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  gobject_class->finalize     = gtk_im_context_ime_finalize;
+  gobject_class->dispose      = gtk_im_context_ime_dispose;
+  gobject_class->set_property = gtk_im_context_ime_set_property;
+  gobject_class->get_property = gtk_im_context_ime_get_property;
+
+  im_context_class->set_client_window   = gtk_im_context_ime_set_client_window;
+  im_context_class->filter_keypress     = gtk_im_context_ime_filter_keypress;
+  im_context_class->reset               = gtk_im_context_ime_reset;
+  im_context_class->get_preedit_string  = gtk_im_context_ime_get_preedit_string;
+  im_context_class->focus_in            = gtk_im_context_ime_focus_in;
+  im_context_class->focus_out           = gtk_im_context_ime_focus_out;
+  im_context_class->set_cursor_location = gtk_im_context_ime_set_cursor_location;
+  im_context_class->set_use_preedit     = gtk_im_context_ime_set_use_preedit;
+}
+
+
+static void
+gtk_im_context_ime_init (GtkIMContextIME *context_ime)
+{
+  context_ime->client_window          = NULL;
+  context_ime->toplevel               = NULL;
+  context_ime->use_preedit            = TRUE;
+  context_ime->preediting             = FALSE;
+  context_ime->opened                 = FALSE;
+  context_ime->focus                  = FALSE;
+  context_ime->cursor_location.x      = 0;
+  context_ime->cursor_location.y      = 0;
+  context_ime->cursor_location.width  = 0;
+  context_ime->cursor_location.height = 0;
+
+  context_ime->priv = g_malloc0 (sizeof (GtkIMContextIMEPrivate));
+  context_ime->priv->conversion_mode  = 0;
+  context_ime->priv->sentence_mode    = 0;
+  context_ime->priv->comp_str         = NULL;
+  context_ime->priv->comp_str_len     = 0;
+  context_ime->priv->read_str         = NULL;
+  context_ime->priv->read_str_len     = 0;
+}
+
+
+static void
+gtk_im_context_ime_dispose (GObject *obj)
+{
+  GtkIMContext *context = GTK_IM_CONTEXT (obj);
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj);
+
+  if (context_ime->client_window)
+    gtk_im_context_ime_set_client_window (context, NULL);
+
+  FREE_PREEDIT_BUFFER (context_ime);
+
+  if (G_OBJECT_CLASS (parent_class)->dispose)
+    G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+
+static void
+gtk_im_context_ime_finalize (GObject *obj)
+{
+  /* GtkIMContext *context = GTK_IM_CONTEXT (obj); */
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj);
+
+  g_free (context_ime->priv);
+  context_ime->priv = NULL;
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+
+static void
+gtk_im_context_ime_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object);
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+  switch (prop_id)
+    {
+    default:
+      break;
+    }
+}
+
+
+static void
+gtk_im_context_ime_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object);
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+  switch (prop_id)
+    {
+    default:
+      break;
+    }
+}
+
+
+GtkIMContext *
+gtk_im_context_ime_new (void)
+{
+  return g_object_new (GTK_TYPE_IM_CONTEXT_IME, NULL);
+}
+
+
+static void
+gtk_im_context_ime_set_client_window (GtkIMContext *context,
+                                      GdkWindow    *client_window)
+{
+  GtkIMContextIME *context_ime;
+  HWND hwnd = NULL;
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+  context_ime = GTK_IM_CONTEXT_IME (context);
+
+  if (client_window)
+    {
+      HIMC himc;
+
+      hwnd = GDK_WINDOW_HWND (client_window);
+
+      /* get default ime context */
+      himc = ImmGetContext (hwnd);
+      context_ime->opened = ImmGetOpenStatus (himc);
+      ImmGetConversionStatus (himc,
+                              &context_ime->priv->conversion_mode,
+                              &context_ime->priv->sentence_mode);
+      ImmReleaseContext (hwnd, himc);
+
+    }
+  else if (context_ime->focus)
+    {
+      gtk_im_context_ime_focus_out (context);
+    }
+
+  context_ime->client_window = client_window;
+}
+
+
+static gboolean
+gtk_im_context_ime_filter_keypress (GtkIMContext *context,
+                                    GdkEventKey  *event)
+{
+  GtkIMContextIME *context_ime;
+  HWND hwnd;
+  HIMC himc;
+  gboolean retval = FALSE;
+
+  g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context), FALSE);
+  g_return_val_if_fail (event, FALSE);
+
+  if (event->type == GDK_KEY_RELEASE)
+    return retval;
+
+  context_ime = GTK_IM_CONTEXT_IME (context);
+  if (!context_ime->focus)
+    return FALSE;
+  if (!GDK_IS_WINDOW (context_ime->client_window))
+    return FALSE;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+
+  /* FIXME!! event->string is deprecated */
+  if (event->string && *event->string
+            && !g_unichar_iscntrl (g_utf8_get_char_validated (event->string,
+                                                    strlen (event->string))))
+    {
+      g_signal_emit_by_name (G_OBJECT (context_ime), "commit", event->string);
+      retval = TRUE;
+    }
+
+  ImmReleaseContext (hwnd, himc);
+
+  return retval;
+}
+
+
+static void
+gtk_im_context_ime_reset (GtkIMContext *context)
+{
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+  HWND hwnd;
+  HIMC himc;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return;
+
+  if (context_ime->preediting && ImmGetOpenStatus (himc))
+    ImmNotifyIME (himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+
+  context_ime->preediting = FALSE;
+  g_signal_emit_by_name (context, "preedit_changed");
+
+  ImmReleaseContext (hwnd, himc);
+}
+
+
+static gchar *
+get_utf8_preedit_string (GtkIMContextIME *context_ime, gint *pos_ret)
+{
+  gchar *utf8str = NULL;
+  HWND hwnd;
+  HIMC himc;
+  gint pos = 0;
+
+  if (pos_ret)
+    *pos_ret = 0;
+
+  /*
+  g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime),
+  g_strdup (""));
+  */
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+
+  /* shouldn't return NULL */
+  g_return_val_if_fail (himc, g_strdup (""));
+
+  if (context_ime->preediting)
+    {
+      gpointer buf;
+      glong len;
+      GError *error = NULL;
+
+#ifdef UNICODE
+      len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+      buf = g_malloc (len);
+      if (len > 0 && buf)
+        {
+          ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+          len /= sizeof (gunichar2);
+          utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error);
+          if (error)
+            {
+              g_warning ("%s", error->message);
+              g_error_free (error);
+            }
+
+          if (pos_ret)
+            {
+              pos = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+              if (pos < 0 || len < pos)
+                {
+                  g_warning ("ImmGetCompositionString: "
+                             "Invalid cursor position!");
+                  pos = 0;
+                }
+            }
+          g_free (buf);
+        }
+#else /* not UNICODE */
+      len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+      buf = g_malloc (len);
+      if (len > 0 && buf)
+        {
+          ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+          utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+          if (error)
+            {
+              g_warning ("%s", error->message);
+              g_error_free (error);
+            }
+
+          if (pos_ret)
+            {
+              pos = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+              /* get cursor position by offset */
+              if (pos < len && utf8str)
+                {
+                  gchar *tmpstr;
+
+                  tmpstr = g_locale_to_utf8 (buf, pos, NULL, NULL, &error);
+                  if (error)
+                    {
+                      g_warning ("%s", error->message);
+                      g_error_free (error);
+                    }
+                  if (tmpstr)
+                    {
+                      pos = g_utf8_strlen (tmpstr, -1);
+                    }
+                  else
+                    {
+                      pos = 0;
+                    }
+                  g_free (tmpstr);
+                }
+              else if (pos == len && utf8str)
+                {
+                  pos = g_utf8_strlen (utf8str, -1);
+                }
+              else
+                {
+                  g_warning ("ImmGetCompositionString: "
+                             "Invalid cursor position!");
+                  pos = 0;
+                }
+            }
+          g_free (buf);
+        }
+#endif /* not UNICODE */
+    }
+
+  if (!utf8str)
+    {
+      utf8str = g_strdup ("");
+      pos = 0;
+    }
+
+  if (pos_ret)
+    *pos_ret = pos;
+
+  ImmReleaseContext (hwnd, himc);
+
+  return utf8str;
+}
+
+
+static PangoAttrList *
+get_pango_attr_list (GtkIMContextIME *context_ime, const gchar *utf8str)
+{
+  PangoAttrList *attrs = pango_attr_list_new ();
+  HWND hwnd;
+  HIMC himc;
+
+  /* g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime), attr); */
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+
+  g_return_val_if_fail (himc, attrs);
+
+  if (context_ime->preediting)
+    {
+      const gchar *schr = utf8str, *echr;
+      guint8 *buf;
+      guint16 f_red, f_green, f_blue, b_red, b_green, b_blue;
+      glong len, spos = 0, epos, sidx = 0, eidx;
+      PangoAttribute *attr;
+
+      /*
+       *  get attributes list of IME.
+       */
+      len = ImmGetCompositionString (himc, GCS_COMPATTR, NULL, 0);
+      buf = g_malloc (len);
+      ImmGetCompositionString (himc, GCS_COMPATTR, buf, len);
+
+      /*
+       *  schr and echr are pointer in utf8str.
+       */
+      for (echr = g_utf8_next_char (utf8str); *schr != '\0';
+           echr = g_utf8_next_char (echr))
+        {
+          /*
+           *  spos and epos are buf(attributes list of IME) position by
+           *  bytes.
+           *  If UNICODE is defined, this value is same with UTF-8 offset.
+           *  If it's not defined, this value is same with bytes position
+           *  of locale encoded preedit string.
+           *
+           */
+#ifdef UNICODE
+          epos = g_utf8_pointer_to_offset (utf8str, echr);
+#else /* not UNICODE */
+          gchar *tmpstr;
+          GError *error = NULL;
+
+          epos = spos;
+          tmpstr = g_locale_from_utf8 (schr, echr - schr, NULL, NULL, &error);
+          if (error)
+            {
+              g_warning ("%s", error->message);
+              g_error_free (error);
+            }
+          if (tmpstr)
+            {
+              epos += strlen (tmpstr);
+              g_free (tmpstr);
+            }
+#endif /* not UNICODE */
+          /*
+           *  sidx and eidx are positions in utf8str by bytes.
+           */
+          eidx = echr - utf8str;
+
+          /*
+           *  convert attributes list to PangoAttriute.
+           */
+          if (*echr == '\0' || buf[spos] != buf[epos])
+            {
+              switch (buf[spos])
+                {
+                case ATTR_TARGET_CONVERTED:
+                  attr = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
+                  attr->start_index = sidx;
+                  attr->end_index = eidx;
+                  pango_attr_list_change (attrs, attr);
+                  f_red = f_green = f_blue = 0;
+                  b_red = b_green = b_blue = 0xffff;
+                  break;
+                case ATTR_TARGET_NOTCONVERTED:
+                  f_red = f_green = f_blue = 0xffff;
+                  b_red = b_green = b_blue = 0;
+                  break;
+                case ATTR_INPUT_ERROR:
+                  f_red = f_green = f_blue = 0;
+                  b_red = b_green = b_blue = 0x7fff;
+                  break;
+                default:        /* ATTR_INPUT,ATTR_CONVERTED,ATTR_FIXEDCONVERTED */
+                  attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+                  attr->start_index = sidx;
+                  attr->end_index = eidx;
+                  pango_attr_list_change (attrs, attr);
+                  f_red = f_green = f_blue = 0;
+                  b_red = b_green = b_blue = 0xffff;
+                }
+              attr = pango_attr_foreground_new (f_red, f_green, f_blue);
+              attr->start_index = sidx;
+              attr->end_index = eidx;
+              pango_attr_list_change (attrs, attr);
+              attr = pango_attr_background_new (b_red, b_green, b_blue);
+              attr->start_index = sidx;
+              attr->end_index = eidx;
+              pango_attr_list_change (attrs, attr);
+
+              schr = echr;
+              spos = epos;
+              sidx = eidx;
+            }
+        }
+      g_free (buf);
+    }
+
+  ImmReleaseContext (hwnd, himc);
+
+  return attrs;
+}
+
+
+static void
+gtk_im_context_ime_get_preedit_string (GtkIMContext   *context,
+                                       gchar         **str,
+                                       PangoAttrList **attrs,
+                                       gint           *cursor_pos)
+{
+  gchar *utf8str = NULL;
+  gint pos = 0;
+  GtkIMContextIME *context_ime;
+
+  context_ime = GTK_IM_CONTEXT_IME (context);
+
+  utf8str = get_utf8_preedit_string (context_ime, &pos);
+
+  if (attrs)
+    *attrs = get_pango_attr_list (context_ime, utf8str);
+
+  if (str)
+    {
+      *str = utf8str;
+    }
+  else
+    {
+      g_free (utf8str);
+      utf8str = NULL;
+    }
+
+  if (cursor_pos)
+    *cursor_pos = pos;
+}
+
+
+static void
+gtk_im_context_ime_focus_in (GtkIMContext *context)
+{
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+  GdkWindow *toplevel;
+  GtkWidget *widget = NULL;
+  HWND hwnd, top_hwnd;
+  HIMC himc;
+
+  if (!GDK_IS_WINDOW (context_ime->client_window))
+    return;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return;
+
+  toplevel = gdk_window_get_toplevel (context_ime->client_window);
+  if (GDK_IS_WINDOW (toplevel))
+    {
+      gdk_window_add_filter (toplevel,
+                             gtk_im_context_ime_message_filter, context_ime);
+      top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+      context_ime->toplevel = toplevel;
+    }
+  else
+    {
+      g_warning ("gtk_im_context_ime_focus_in(): "
+                 "cannot find toplevel window.");
+      return;
+    }
+
+  /* trace reparenting (probably no need) */
+  gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+  if (GTK_IS_WIDGET (widget))
+    {
+      g_signal_connect (G_OBJECT (widget), "hierarchy-changed",
+                        G_CALLBACK (cb_client_widget_hierarchy_changed),
+                        context_ime);
+    }
+  else
+    {
+      /* warning? */
+    }
+
+  /* swtich current context */
+  context_ime->focus = TRUE;
+
+  /* restore preedit context */
+  ImmSetConversionStatus (himc,
+                          context_ime->priv->conversion_mode,
+                          context_ime->priv->sentence_mode);
+
+  if (context_ime->opened)
+    {
+      if (!ImmGetOpenStatus (himc))
+        ImmSetOpenStatus (himc, TRUE);
+      if (context_ime->preediting)
+        {
+          ImmSetCompositionString (himc,
+                                   SCS_SETSTR,
+                                   context_ime->priv->comp_str,
+                                   context_ime->priv->comp_str_len, NULL, 0);
+          FREE_PREEDIT_BUFFER (context_ime);
+        }
+    }
+
+  /* clean */
+  ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_focus_out (GtkIMContext *context)
+{
+  GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+  GdkWindow *toplevel;
+  GtkWidget *widget = NULL;
+  HWND hwnd, top_hwnd;
+  HIMC himc;
+
+  if (!GDK_IS_WINDOW (context_ime->client_window))
+    return;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return;
+
+  /* save preedit context */
+  ImmGetConversionStatus (himc,
+                          &context_ime->priv->conversion_mode,
+                          &context_ime->priv->sentence_mode);
+
+  if (ImmGetOpenStatus (himc))
+    {
+      gboolean preediting = context_ime->preediting;
+
+      if (preediting)
+        {
+          FREE_PREEDIT_BUFFER (context_ime);
+
+          context_ime->priv->comp_str_len
+            = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+          context_ime->priv->comp_str
+            = g_malloc (context_ime->priv->comp_str_len);
+          ImmGetCompositionString (himc, GCS_COMPSTR,
+                                   context_ime->priv->comp_str,
+                                   context_ime->priv->comp_str_len);
+
+          context_ime->priv->read_str_len
+            = ImmGetCompositionString (himc, GCS_COMPREADSTR, NULL, 0);
+          context_ime->priv->read_str
+            = g_malloc (context_ime->priv->read_str_len);
+          ImmGetCompositionString (himc, GCS_COMPREADSTR,
+                                   context_ime->priv->read_str,
+                                   context_ime->priv->read_str_len);
+        }
+
+      ImmSetOpenStatus (himc, FALSE);
+
+      context_ime->opened = TRUE;
+      context_ime->preediting = preediting;
+    }
+  else
+    {
+      context_ime->opened = FALSE;
+      context_ime->preediting = FALSE;
+    }
+
+  /* remove signal handler */
+  gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+  if (GTK_IS_WIDGET (widget))
+    {
+      g_signal_handlers_disconnect_by_func
+        (G_OBJECT (widget),
+         G_CALLBACK (cb_client_widget_hierarchy_changed), context_ime);
+    }
+
+  /* remove event fileter */
+  toplevel = gdk_window_get_toplevel (context_ime->client_window);
+  if (GDK_IS_WINDOW (toplevel))
+    {
+      gdk_window_remove_filter (toplevel,
+                                gtk_im_context_ime_message_filter,
+                                context_ime);
+      top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+      context_ime->toplevel = NULL;
+    }
+  else
+    {
+      g_warning ("gtk_im_context_ime_focus_out(): "
+                 "cannot find toplevel window.");
+    }
+
+  /* swtich current context */
+  context_ime->focus = FALSE;
+
+  /* clean */
+  ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_cursor_location (GtkIMContext *context,
+                                        GdkRectangle *area)
+{
+  gint wx = 0, wy = 0;
+  GtkIMContextIME *context_ime;
+  COMPOSITIONFORM cf;
+  HWND hwnd;
+  HIMC himc;
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+  context_ime = GTK_IM_CONTEXT_IME (context);
+  if (area)
+    context_ime->cursor_location = *area;
+
+  if (!context_ime->client_window)
+    return;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return;
+
+  get_window_position (context_ime->client_window, &wx, &wy);
+  cf.dwStyle = CFS_POINT;
+  cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+  cf.ptCurrentPos.y = wy + context_ime->cursor_location.y;
+  ImmSetCompositionWindow (himc, &cf);
+
+  ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_use_preedit (GtkIMContext *context,
+                                    gboolean      use_preedit)
+{
+  GtkIMContextIME *context_ime;
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+  context_ime = GTK_IM_CONTEXT_IME (context);
+
+  context_ime->use_preedit = use_preedit;
+  if (context_ime->preediting)
+    {
+      /* FIXME */
+      HWND hwnd;
+      HIMC himc;
+
+      hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+      himc = ImmGetContext (hwnd);
+      if (!himc)
+        return;
+    }
+}
+
+
+static void
+gtk_im_context_ime_set_preedit_font (GtkIMContext *context, PangoFont *font)
+{
+  GtkIMContextIME *context_ime;
+  GtkWidget *widget = NULL;
+  HWND hwnd;
+  HIMC himc;
+  PangoContext *pango_context;
+  LOGFONT *logfont;
+
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+  context_ime = GTK_IM_CONTEXT_IME (context);
+  if (!context_ime->client_window)
+    return;
+
+  gdk_window_get_user_data (context_ime->client_window, (gpointer) &widget);
+  if (!GTK_IS_WIDGET (widget))
+    return;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return;
+
+  /* set font */
+  pango_context = gtk_widget_get_pango_context (widget);
+  if (!pango_context)
+    goto ERROR_OUT;
+
+  if (!font)
+    font = pango_context_load_font (pango_context, widget->style->font_desc);
+  if (!font)
+    goto ERROR_OUT;
+
+  logfont = pango_win32_font_logfont (font);
+  if (logfont)
+    ImmSetCompositionFont (himc, logfont);
+
+ERROR_OUT:
+  /* clean */
+  ImmReleaseContext (hwnd, himc);
+}
+
+
+static GdkFilterReturn
+gtk_im_context_ime_message_filter (GdkXEvent *xevent,
+                                   GdkEvent  *event,
+                                   gpointer   data)
+{
+  GtkIMContext *context;
+  GtkIMContextIME *context_ime;
+  HWND hwnd;
+  HIMC himc;
+  MSG *msg = (MSG *) xevent;
+  GdkFilterReturn retval = GDK_FILTER_CONTINUE;
+
+  g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (data), retval);
+
+  context = GTK_IM_CONTEXT (data);
+  context_ime = GTK_IM_CONTEXT_IME (data);
+  if (!context_ime->focus)
+    return retval;
+
+  hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+  himc = ImmGetContext (hwnd);
+  if (!himc)
+    return retval;
+
+  switch (msg->message)
+    {
+    case WM_IME_COMPOSITION:
+      {
+        gint wx = 0, wy = 0;
+        CANDIDATEFORM cf;
+
+        get_window_position (context_ime->client_window, &wx, &wy);
+        /* FIXME! */
+        {
+          HWND hwnd_top;
+          POINT pt;
+          RECT rc;
+
+          hwnd_top =
+            GDK_WINDOW_HWND (gdk_window_get_toplevel
+                             (context_ime->client_window));
+          GetWindowRect (hwnd_top, &rc);
+          pt.x = wx;
+          pt.y = wy;
+          ClientToScreen (hwnd_top, &pt);
+          wx = pt.x - rc.left;
+          wy = pt.y - rc.top;
+        }
+        cf.dwIndex = 0;
+        cf.dwStyle = CFS_CANDIDATEPOS;
+        cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+        cf.ptCurrentPos.y = wy + context_ime->cursor_location.y
+          + context_ime->cursor_location.height;
+        ImmSetCandidateWindow (himc, &cf);
+
+        if ((msg->lParam & GCS_COMPSTR))
+          g_signal_emit_by_name (context, "preedit_changed");
+
+        if (msg->lParam & GCS_RESULTSTR)
+          {
+            gsize len;
+            gpointer buf;
+            gchar *utf8str = NULL;
+            GError *error = NULL;
+
+            len = ImmGetCompositionString (himc, GCS_RESULTSTR, NULL, 0);
+            buf = g_alloca (len);
+            if (len > 0 && buf)
+              {
+                ImmGetCompositionString (himc, GCS_RESULTSTR, buf, len);
+#ifdef UNICODE
+                len /= sizeof (gunichar2);
+                utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error);
+#else /* not UNICODE */
+                utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+#endif /* not UNICODE */
+                if (error)
+                  {
+                    g_warning ("%s", error->message);
+                    g_error_free (error);
+                  }
+              }
+
+            if (utf8str)
+              {
+                g_signal_emit_by_name (G_OBJECT (context), "commit", utf8str);
+                g_free (utf8str);
+              }
+          }
+
+        if (context_ime->use_preedit)
+          retval = TRUE;
+        break;
+      }
+
+    case WM_IME_STARTCOMPOSITION:
+      context_ime->preediting = TRUE;
+      gtk_im_context_ime_set_cursor_location (context, NULL);
+      g_signal_emit_by_name (context, "preedit_start");
+      if (context_ime->use_preedit)
+        retval = TRUE;
+      break;
+
+    case WM_IME_ENDCOMPOSITION:
+      context_ime->preediting = FALSE;
+      g_signal_emit_by_name (context, "preedit_changed");
+      g_signal_emit_by_name (context, "preedit_end");
+      if (context_ime->use_preedit)
+        retval = TRUE;
+      break;
+
+    case WM_IME_NOTIFY:
+      switch (msg->wParam)
+        {
+        case IMN_SETOPENSTATUS:
+          context_ime->opened = ImmGetOpenStatus (himc);
+          gtk_im_context_ime_set_preedit_font (context, NULL);
+          break;
+
+        default:
+          break;
+        }
+
+    default:
+      break;
+    }
+
+  ImmReleaseContext (hwnd, himc);
+  return retval;
+}
+
+
+/*
+ * x and y must be initialized to 0.
+ */
+static void
+get_window_position (GdkWindow *win, gint *x, gint *y)
+{
+  GdkWindow *parent, *toplevel;
+  gint wx, wy;
+
+  g_return_if_fail (GDK_IS_WINDOW (win));
+  g_return_if_fail (x && y);
+
+  gdk_window_get_position (win, &wx, &wy);
+  *x += wx;
+  *y += wy;
+  parent = gdk_window_get_parent (win);
+  toplevel = gdk_window_get_toplevel (win);
+
+  if (parent && parent != toplevel)
+    get_window_position (parent, x, y);
+}
+
+
+/*
+ *  probably, this handler isn't needed.
+ */
+static void
+cb_client_widget_hierarchy_changed (GtkWidget       *widget,
+                                    GtkWidget       *widget2,
+                                    GtkIMContextIME *context_ime)
+{
+  GdkWindow *new_toplevel;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+  if (!context_ime->client_window)
+    return;
+  if (!context_ime->focus)
+    return;
+
+  new_toplevel = gdk_window_get_toplevel (context_ime->client_window);
+  if (context_ime->toplevel == new_toplevel)
+    return;
+
+  /* remove filter from old toplevel */
+  if (GDK_IS_WINDOW (context_ime->toplevel))
+    {
+      gdk_window_remove_filter (context_ime->toplevel,
+                                gtk_im_context_ime_message_filter,
+                                context_ime);
+    }
+  else
+    {
+    }
+
+  /* add filter to new toplevel */
+  if (GDK_IS_WINDOW (new_toplevel))
+    {
+      gdk_window_add_filter (new_toplevel,
+                             gtk_im_context_ime_message_filter, context_ime);
+    }
+  else
+    {
+    }
+
+  context_ime->toplevel = new_toplevel;
+}
diff --git a/modules/input/gtkimcontextime.h b/modules/input/gtkimcontextime.h
new file mode 100644 (file)
index 0000000..e4d97d9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ * Copyright (C) 2003 Kazuki IWAMOTO
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include <gtk/gtkimcontext.h>
+
+extern GType gtk_type_im_context_ime;
+
+#define GTK_TYPE_IM_CONTEXT_IME            gtk_type_im_context_ime
+#define GTK_IM_CONTEXT_IME(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIME))
+#define GTK_IM_CONTEXT_IME_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIMEClass))
+#define GTK_IS_IM_CONTEXT_IME(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_CONTEXT_IME))
+#define GTK_IS_IM_CONTEXT_IME_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_IME))
+#define GTK_IM_CONTEXT_IME_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIMEClass))
+
+typedef struct _GtkIMContextIME GtkIMContextIME;
+typedef struct _GtkIMContextIMEPrivate GtkIMContextIMEPrivate;
+typedef struct _GtkIMContextIMEClass GtkIMContextIMEClass;
+
+struct _GtkIMContextIME
+{
+  GtkIMContext object;
+
+  GdkWindow *client_window;
+  GdkWindow *toplevel;
+  guint use_preedit : 1;
+  guint preediting : 1;
+  guint opened : 1;
+  guint focus : 1;
+  GdkRectangle cursor_location;
+
+  GtkIMContextIMEPrivate *priv;
+};
+
+struct _GtkIMContextIMEClass
+{
+  GtkIMContextClass parent_class;
+};
+
+
+void          gtk_im_context_ime_register_type (GTypeModule * type_module);
+GtkIMContext *gtk_im_context_ime_new           (void);
diff --git a/modules/input/imime.c b/modules/input/imime.c
new file mode 100644 (file)
index 0000000..6100876
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include <gtk/gtkimmodule.h>
+#include "gtkimcontextime.h"
+
+#include <string.h>
+
+static const GtkIMContextInfo ime_info = {
+  "win32ime",
+  "Windows IME",
+  "gtk+",
+  "",
+  "*",
+};
+
+static const GtkIMContextInfo *info_list[] = {
+  &ime_info,
+};
+
+void
+im_module_init (GTypeModule * module)
+{
+  gtk_im_context_ime_register_type (module);
+}
+
+void
+im_module_exit (void)
+{
+}
+
+void
+im_module_list (const GtkIMContextInfo *** contexts, int *n_contexts)
+{
+  *contexts = info_list;
+  *n_contexts = G_N_ELEMENTS (info_list);
+}
+
+GtkIMContext *
+im_module_create (const gchar * context_id)
+{
+  g_return_val_if_fail (context_id, NULL);
+
+  if (!strcmp (context_id, "win32ime"))
+    return g_object_new (GTK_TYPE_IM_CONTEXT_IME, NULL);
+  else
+    return NULL;
+}
diff --git a/modules/input/imm-extra.h b/modules/input/imm-extra.h
new file mode 100644 (file)
index 0000000..7bed406
--- /dev/null
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* $Id$ */
+
+
+#include <windows.h>
+#include <imm.h>
+
+
+/* these aren't defined in Cygwin's imm.h */
+
+#ifndef WM_IME_REQUEST
+#   define WM_IME_REQUEST                  0x0288
+#endif  /* WM_IME_REQUEST */
+
+#ifndef IMR_COMPOSITIONWINDOW
+#   define IMR_COMPOSITIONWINDOW           0x0001
+#endif /* IMR_COMPOSITIONWINDOW */
+
+#ifndef IMR_CANDIDATEWINDOW
+#   define IMR_CANDIDATEWINDOW             0x0002
+#endif /* IMR_CANDIDATEWINDOW */
+
+#ifndef IMR_COMPOSITIONFONT
+#   define IMR_COMPOSITIONFONT             0x0003
+#endif /* IMR_COMPOSITIONFONT */
+
+#ifndef IMR_RECONVERTSTRING
+#   define IMR_RECONVERTSTRING             0x0004
+#endif /* IMR_RECONVERTSTRING */
+
+#ifndef IMR_CONFIRMRECONVERTSTRING
+#   define IMR_CONFIRMRECONVERTSTRING      0x0005
+#endif /* IMR_CONFIRMRECONVERTSTRING */
+
+#ifndef IMR_QUERYCHARPOSITION
+#   define IMR_QUERYCHARPOSITION           0x0006
+typedef struct tagIMECHARPOSITION {
+  DWORD  dwSize;
+  DWORD  dwCharPos;
+  POINT  pt;
+  UINT   cLineHeight;
+  RECT   rcDocument;
+} IMECHARPOSITION, *PIMECHARPOSITION;
+#endif /* IMR_QUERYCHARPOSITION */
+
+#ifndef IMR_DOCUMENTFEED
+#   define IMR_DOCUMENTFEED                0x0007
+#endif /* IMR_DOCUMENTFEED */